package org.example.service;

import javafx.collections.ObservableList;
import org.example.controller.JavaProcessManagementController;
import org.example.model.ApplicationProcess;
import org.example.utils.CmdUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author wwy
 * @date 2020-11-29 15:25
 */
@Service
public class ApplicationStatusService {
    private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationStatusService.class);
    private CmdUtils cmdUtils = new CmdUtils();
    /**
     * 所有jar应用pid
     */
    public static ConcurrentHashMap<String,String> appPidMap = new ConcurrentHashMap<>();
    /**
     * app状态检测
     */
    public void statusDetection(){
        this.jspList();
        Map<String, ObservableList<ApplicationProcess>> applicationDataMap = JavaProcessManagementController.applicationDataMap;
        for (Map.Entry<String, ObservableList<ApplicationProcess>> stringObservableListEntry : applicationDataMap.entrySet()) {
            ObservableList<ApplicationProcess> value = stringObservableListEntry.getValue();
            String key = stringObservableListEntry.getKey();
            for (ApplicationProcess applicationProcess : value) {
                String appName = applicationProcess.getAppName();
                String appAddress = applicationProcess.getAppAddress();
                if(appName.contains(".jar") || appName.contains(".JAR")){
                    for (Map.Entry<String, String> stringStringEntry : appPidMap.entrySet()) {
                        String value1 = stringStringEntry.getValue();
                        String key1 = stringStringEntry.getKey();
                        if(value1.equals(appAddress)){
                            //判断是否存在监听端口
                            int i = this.listenPortByPid(key1, applicationProcess.getListenPort());
                            if(i == 0){
                                //启动成功
                                applicationProcess.setStatusName("启动成功");
                                applicationProcess.setPid(key1);
                            }else{
                                //正在启动
                                applicationProcess.setStatusName("正在启动");
                                applicationProcess.setPid(key1);
                            }

                            break;
                        }
                        applicationProcess.setStatusName("关闭");
                        applicationProcess.setPid("");
                    }
                    continue;
                }
                String applicationPid = this.getApplicationPid(applicationProcess.getAppName(), applicationProcess.getAppAddress());
                if(applicationPid == null){
                    applicationProcess.setPid("");
                    applicationProcess.setStatusName("关闭");
                }else{
                    applicationProcess.setPid(applicationPid);
                    applicationProcess.setStatusName("启动");
                }
            }
        }

    }

    /**
     * jar程序状态
     * javaw -jar ****.jar
     * @param appName
     * @param appAddress
     * @return
     */
    public boolean applicationStatusByJar(String appName, String appAddress) {
        for (Map.Entry<String, String> stringStringEntry : appPidMap.entrySet()) {
            if(stringStringEntry.getValue().equals(appAddress)){
                return true;
            }
        }
        return false;
    }

    /**
     * 判断该pid是否存在监听端口号
     * netstat -aon | findstr "8003"
     * @param pid
     */
    public int listenPortByPid(String pid,String port){
        List<String> commend = new ArrayList<String>();
        commend.add("netstat");
        commend.add("-aon");
        /*commend.add("\""+port+"\"");*/
        String outstring = null;
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commend);
            //将标准输入流和错误输入流合并，通过标准输入流程读取信息
            builder.redirectErrorStream(true);
            Process p = builder.start();
            outstring = cmdUtils.waitFor(p);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        if(outstring == null || outstring.equals("error")){
            LOGGER.info("无该监听端口的应用");
        }else{
            System.out.println(outstring);
            //是否包含pid,包含pid启动成功
            if(outstring.contains(pid)){
                //启动成功
                return 0;
            }
        }
        return 1;
    }

    /**
     * jps -l 命令结果集
     * @return
     */
    private Map<String,String> jspList(){
        appPidMap.clear();
        List<String> commend = new ArrayList<String>();
        commend.add("jps");
        commend.add("-l");
        String outstring = null;
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commend);
            //将标准输入流和错误输入流合并，通过标准输入流程读取信息
            builder.redirectErrorStream(true);
            Process p = builder.start();
            outstring = cmdUtils.waitFor(p);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
        if(outstring == null || outstring.equals("error")){
            LOGGER.info("获取应用状态失败");
        }else{
            System.out.println(outstring);

            String[] rowLineArray = outstring.split("\n");
            for (String rowLine : rowLineArray) {
                String[] s = rowLine.split(" ");
                if(s.length == 1){
                    continue;
                }
                appPidMap.put(s[0],s[1]);
            }
        }
        return appPidMap;
    }

    /**
     * 软件是否启动
     * @param appName
     * @return
     */
    public boolean applicationStatus(String appName,String path){
        List<String> commend = new ArrayList<String>();
        commend.add("tasklist");
        commend.add("/fi");
        commend.add("\"imagename");
        commend.add("eq");
        commend.add(appName+"\"");
        commend.add("/fo");
        commend.add("list");
        String outstring = null;
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commend);
            //将标准输入流和错误输入流合并，通过标准输入流程读取信息
            builder.redirectErrorStream(true);
            Process p = builder.start();
            outstring = cmdUtils.waitFor(p);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
        if(outstring == null || outstring.equals("error")){
            LOGGER.info("获取应用启动状态失败");
            return false;
        }else{
            String outStringTs = outstring.replace(" ", "");
            System.out.println(outStringTs);
            if(outStringTs.contains("映像名称")){
                String[] lineStrArray = outStringTs.split("\n");
                //同名称app启动数目
                int appSize = lineStrArray.length / 6;
                for (int i = 0; i < appSize; i++) {
                    String imgName = lineStrArray[(i*6)+1].split(":")[1];
                    String pid = lineStrArray[(i*6)+2].split(":")[1];
                    String memoryUse = lineStrArray[(i*6)+5].split(":")[1];
                    LOGGER.info("映像名称:{},pid:{},,内存使用：{}",imgName,pid,memoryUse);
                    //根据pid获取应用路径
                    String pathByPid = this.getPathByPid(pid);
                    if(pathByPid == null){
                        //系统错误

                    }else if(pathByPid.equals(path)){
                        //软件已启动
                        return true;
                    }
                }

                return false;
            }else{
                return false;
            }

        }

    }


    /**
     * 软件是否启动
     * @param appName
     * @return
     */
    public String getApplicationPid(String appName,String path){
        List<String> commend = new ArrayList<String>();
        commend.add("tasklist");
        commend.add("/fi");
        commend.add("\"imagename");
        commend.add("eq");
        commend.add(appName+"\"");
        commend.add("/fo");
        commend.add("list");
        String outstring = null;
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commend);
            //将标准输入流和错误输入流合并，通过标准输入流程读取信息
            builder.redirectErrorStream(true);
            Process p = builder.start();
            outstring = cmdUtils.waitFor(p);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
        if(outstring == null || outstring.equals("error")){
            LOGGER.info("获取应用启动状态失败");
            return null;
        }else{
            String outStringTs = outstring.replace(" ", "");
            System.out.println(outStringTs);
            if(outStringTs.contains("映像名称")){
                String[] lineStrArray = outStringTs.split("\n");
                //同名称app启动数目
                int appSize = lineStrArray.length / 6;
                for (int i = 0; i < appSize; i++) {
                    String imgName = lineStrArray[(i*6)+1].split(":")[1];
                    String pid = lineStrArray[(i*6)+2].split(":")[1];
                    String memoryUse = lineStrArray[(i*6)+5].split(":")[1];
                    LOGGER.info("映像名称:{},pid:{},,内存使用：{}",imgName,pid,memoryUse);
                    //根据pid获取应用路径
                    String pathByPid = this.getPathByPid(pid);
                    if(pathByPid == null){
                        //系统错误

                    }else if(pathByPid.equals(path)){
                        //软件已启动
                        return pid;
                    }
                }

                return null;
            }else{
                return null;
            }

        }
    }

    /**
     * 根据pid查询软件所在的位置
     * wmic process where processid="10304"  get executablepath
     * @param pid
     * @return
     */
    public String getPathByPid(String pid){
        List<String> commend = new ArrayList<String>();
        commend.add("wmic");
        commend.add("process");
        commend.add("where");
        commend.add("processid=\""+pid+"\"");
        commend.add("get");
        commend.add("executablepath");
        String outstring = null;
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commend);
            //将标准输入流和错误输入流合并，通过标准输入流程读取信息
            builder.redirectErrorStream(true);
            Process p = builder.start();
            outstring = cmdUtils.waitFor(p);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
        if(outstring == null || outstring.equals("error")){
            LOGGER.info("获取进程路径失败");

        }else{
            boolean executablePathBool = outstring.contains("ExecutablePath");
            System.out.println(outstring);
            if(executablePathBool){
                //结果正确
                String[] split = outstring.split("\n\n");
                return split[1].replace(" ","");
            }else{
                //结果错误
                return null;
            }


        }
        return null;
    }

    public Map<String, String> getAppPidMap() {
        return appPidMap;
    }

    public void setAppPidMap(Map<String, String> appPidMap) {
        appPidMap = appPidMap;
    }
}
